#include "ComponentRunAwayBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentCreatureSounds.h"
#include "ComponentPathfinding.h"
#include "ComponentHerdBehavior.h"

#include "SubsystemNoise.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"
#include "Managers/BlocksManager.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"
#include "Segment2.h"

#include <iostream>
#include <cfloat>


namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentRunAwayBehavior::StateMachine::StateMachine(ComponentRunAwayBehavior *runAwayBehavior) : m_runAwayBehavior(runAwayBehavior), EasyMachine(MachineState::Inactive)
    {
        enter();
    }

    void ComponentRunAwayBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                m_runAwayBehavior->m_importanceLevel = 0.0f;
                m_runAwayBehavior->m_lastNoiseSourcePosition = FastVector3::Zero;
            }
            break;
        case MachineState::RunningAway:
            MACHINE_DELEGATE()
            {
                Vector3 value = m_runAwayBehavior->findSafePlace();
                m_runAwayBehavior->componentPathfinding->setDestination(value, 1.0f, 1.0f, 0, false, true, false, nullptr);
                // 播放声音
                
                // 广播噪声
                m_runAwayBehavior->subsystemNoise->makeNoise(m_runAwayBehavior->componentCreature->componentBody, 0.25f, 6.0f);
            }
            break;
        }
    }

    void ComponentRunAwayBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                if (m_runAwayBehavior->m_attacker != entt::null)
                {
                    m_runAwayBehavior->m_timeToForgetAttacker -= dt;
                    if (m_runAwayBehavior->m_timeToForgetAttacker <= 0.0f)
                    {
                        m_runAwayBehavior->m_attacker = entt::null;
                    }
                }
                if (m_runAwayBehavior->m_attacker != entt::null && GameSingleton::gameRegistry->valid(m_runAwayBehavior->m_attacker))
                {
                    ComponentBody *attackerBody = GameSingleton::gameRegistry->get<ComponentBody*>(m_runAwayBehavior->m_attacker);

                    Vector3 attackerPosition;
                    Vector3 bodyPosition;
                    attackerBody->getPosition(attackerPosition);
                    m_runAwayBehavior->componentCreature->componentBody->getPosition(bodyPosition);

                    if (m_runAwayBehavior->componentCreature->componentHealth->healthChange < 0.0f || 
                        Vector3::DistanceSquared(attackerPosition, bodyPosition) < 36.0)
                    {
                        m_runAwayBehavior->m_importanceLevel = MathUtils::Max<float>(m_runAwayBehavior->m_importanceLevel, (m_runAwayBehavior->componentCreature->componentHealth->health < 0.33f) ? 300 : 100);
                    }
                }
                else
                {
                    m_runAwayBehavior->m_attacker = entt::null;
                }

                if (m_runAwayBehavior->m_heardNoise)
                {
                    m_runAwayBehavior->m_importanceLevel = MathUtils::Max<float>(m_runAwayBehavior->m_importanceLevel, 5.0f);
                }
                else if (m_runAwayBehavior->isActive() == false)
                {
                    m_runAwayBehavior->m_importanceLevel = 0.0f;
                }
                if (m_runAwayBehavior->isActive())
                {
                    changeTo(MachineState::RunningAway);
                }
            }
            break;
        case MachineState::RunningAway:
            MACHINE_DELEGATE()
            {
                if (m_runAwayBehavior->isActive() == false)
                {
                    changeTo(MachineState::Inactive);
                }
                else if (m_runAwayBehavior->componentPathfinding->destination == FastVector3::Zero || m_runAwayBehavior->componentPathfinding->isStuck)
                {
                    m_runAwayBehavior->m_importanceLevel = 0.0f;
                }
                else if (m_runAwayBehavior->m_attacker != entt::null)
                {
                    if (GameSingleton::gameRegistry->valid(m_runAwayBehavior->m_attacker))
                    {
                        ComponentHealth **healthPtr = GameSingleton::gameRegistry->try_get<ComponentHealth *>(m_runAwayBehavior->m_attacker);
                        if (healthPtr != nullptr && (*healthPtr)->health == 0.0f)
                        {
                            m_runAwayBehavior->m_importanceLevel = 0.0f;
                            m_runAwayBehavior->m_attacker = entt::null;
                        }
                    }
                    else
                    {
                        m_runAwayBehavior->m_attacker = entt::null;
                    }
                }
            }
            break;
        }
    }

    void ComponentRunAwayBehavior::StateMachine::leave() {}

    // **************************************



    ComponentRunAwayBehavior::~ComponentRunAwayBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentRunAwayBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    void ComponentRunAwayBehavior::runAwayFrom(entt::entity attacker)
    {
        m_attacker = attacker;
        m_timeToForgetAttacker = GameSingleton::gameRandom->getFloat(10.0f, 20.0f);
    }

    void ComponentRunAwayBehavior::hearNoise(ComponentBody *sourceBody, const Vector3 &sourcePosition, float loudness)
    {
        if (loudness >= 1.0f)
        {
            m_heardNoise = true;
            m_lastNoiseSourcePosition = sourcePosition;
        }
    }

    Vector3 ComponentRunAwayBehavior::findSafePlace()
    {
        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        Vector3 herdPosition = (componentHerdBehavior != nullptr) ? componentHerdBehavior->findHerdCenter() : FastVector3::Zero;
        if (herdPosition != FastVector3::Zero && Vector3::DistanceSquared(position, herdPosition) < 144.0f)
        {
            herdPosition == FastVector3::Zero;
        }
        float num = FLT_MAX;
        Vector3 result = position;
        for (int32_t i = 0; i < 30; i++)
        {
            int32_t num2 = Terrain::ToCell(position.posX + GameSingleton::gameRandom->getFloat(-25.0f, 25.0f));
            int32_t num3 = Terrain::ToCell(position.posZ + GameSingleton::gameRandom->getFloat(-25.0f, 25.0f));
            for (int32_t num4 = 255; num4 >= 0; num4--)
            {
                int32_t cellContents = subsystemTerrain->getCellContents(num2, num4, num3);
                if (GameSingleton::blocksManager->blocks[cellContents]->IsCollidable || cellContents == 18)
                {
                    Vector3 vector(num2 + 0.5f, num4 + 1.1f, num3 + 0.5f);
                    float num5 = scoreSafePlace(position, vector, herdPosition, m_lastNoiseSourcePosition, cellContents);
                    if (num5 > num)
                    {
                        num = num5;
                        result = vector;
                    }
                    break;
                }
            }
        }
        return result;
    }

    float ComponentRunAwayBehavior::scoreSafePlace(const Vector3&currentPosition, const Vector3& safePosition, const Vector3& herdPosition, const Vector3& noiseSourcePosition, int32_t contents)
    {
        float num = 0.0f;
        Vector2 vector(currentPosition.posX, currentPosition.posZ);
        Vector2 vector2(safePosition.posX, safePosition.posZ);
        Segment2 s(vector, vector2);
        if (m_attacker != entt::null && GameSingleton::gameRegistry->valid(m_attacker))
        {
            ComponentBody *body = GameSingleton::gameRegistry->get<ComponentBody*>(m_attacker);

            Vector3 position;
            body->getPosition(position);
            

            Vector2 vector3(position.posX, position.posZ);
            float num2 = Vector2::Distance(vector3, vector2);
            float num3 = Segment2::Distance(s, vector3);
            num += num2 + 3.0f * num3;
        }
        else
        {
            num += 2.0f * Vector2::Distance(vector, vector2);
        }
        std::unique_ptr<Vector2> vector4 = (herdPosition != FastVector3::Zero) ? std::make_unique<Vector2>(herdPosition.posX, herdPosition.posZ) : nullptr;
        float num4 = (vector4 != nullptr) ? Segment2::Distance(s, *vector4) : 0.0f;
        num -= num4;

        std::unique_ptr<Vector2> vector5 = (noiseSourcePosition != FastVector3::Zero) ? std::make_unique<Vector2>(noiseSourcePosition.posX, noiseSourcePosition.posZ) : nullptr;
        float num5 = (vector5 != nullptr) ? Segment2::Distance(s, *vector5) : 0.0f;
        num += 1.5f * num5;
        if (contents == 18)
        {
            num -= 4.0f;
        }
        return num;
    }

    void ComponentRunAwayBehavior::update(float dt)
    {
        m_stateMachine->update(dt);
        m_heardNoise = false;
    }

    static const std::string componentName = "RunAwayBehavior";
    const std::string &ComponentRunAwayBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentRunAwayBehavior::GetName()
    {
        return componentName;
    }
    void ComponentRunAwayBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature    = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);

        ComponentHerdBehavior **herdPtr = GameSingleton::gameRegistry->try_get<ComponentHerdBehavior *>(entity);
        componentHerdBehavior = (herdPtr != nullptr) ? (*herdPtr) : nullptr;

        subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        subsystemNoise = GameSingleton::singletonPtr->getSingleton<SubsystemNoise>();

        m_stateMachine = new StateMachine(this);
    }

}
